package client;

import generated.*;
import java.util.*;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.Socket;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import KI.KI;
/**
 * Client MazeCom
 * @author b.osterholt s.lucksch t.schroedter
 *
 */

public class Client {
	private String hostname;
	private int port;	
	private Socket client;
	private int id;
	private UTFInputStream inFromServer;
	private UTFOutputStream outToServer;
	//private ClientThread t;
	private JAXBContext jc;
	// Ein Marshaller sorgt dafuer, dass man ein Object in einen Stream schreiben kann
	private Marshaller m; Unmarshaller um;
	// Eine ObjectFactory wurde beim Generieren der JAXB Dateien erstellt und ermoeglicht es die anderen JAXB-Klassen erzeugen zu lassen
	private ObjectFactory of;
	private KI ki;
	
	/**
	 * 
	 * @param argu
	 */
	@SuppressWarnings("unused")
	public static void main(String argu[]) {
		Client c = new Client();
	}
	
	/**
	 * Konstruktor des Clients
	 */
	public Client(){
		
		try {
			jc = JAXBContext.newInstance(MazeCom.class);
			m = jc.createMarshaller();
			m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
			of = new ObjectFactory();
			um = jc.createUnmarshaller();
			
			if (verbinde()) { bearbeite(); }
			else { beende(); }
		} catch (JAXBException e) { e.getLocalizedMessage();
		} catch (IOException e) { e.getLocalizedMessage();
		}
	}

	/**
	 * Verbinde den Client mit dem Server
	 * @return Verbinden erfolgreich
	 */
	public boolean verbinde(){
		port = 5123;
		hostname = "localhost";
		try {
			client = new Socket(hostname, port);
			outToServer = new UTFOutputStream(client.getOutputStream());
			inFromServer = new UTFInputStream(client.getInputStream());
			System.out.println("Verbindung hergestellt zu \""+hostname+"\", Port: "+port);
		
			 boolean login = false;
			 while (!login){
				 //LOGINMESSAGE Senden
				 MazeCom mazeComObj = of.createMazeCom(); // MazeComObjekt wird nachher �bers netz geschickt
				 LoginMessageType loginM = of.createLoginMessageType();
				 loginM.setName("PrincessPower");
				 mazeComObj.setLoginMessage(loginM);
				 mazeComObj.setId(0);
				 mazeComObj.setMcType(MazeComType.fromValue("LOGIN") );
				 
				 StringWriter strWrite = new StringWriter();
				 // Hier wird ein Object "MazeCom" in den out-Stream geschrieben/gemarshaled
				 m.marshal(mazeComObj,strWrite);
				 outToServer.writeUTF8(strWrite.toString());
			 
				 //Recieve Message empfangen
				 StringReader strRead = new StringReader(inFromServer.readUTF8());
				 // Hier wird ein Object recieve erstellt, das aus dem InputStream ge"unmarshal"t wird
				 MazeCom recievedMes= (MazeCom)um.unmarshal(strRead);

				 MazeComType content = recievedMes.getMcType();
				 switch (content) {
				 	case LOGINREPLY:
				 		LoginReplyMessageType lrm = recievedMes.getLoginReplyMessage();
				 		id = lrm.getNewID();	
				 		System.out.println("Login war erfolgreich. Deine ID: "+id);
				 		login = true;
				 		break;
				 	case ACCEPT:
				 		AcceptMessageType amt = recievedMes.getAcceptMessage();
				 		if (!amt.isAccept())
				 			System.out.println("Login failed... "+ amt.getErrorCode());
				 		break;
				 	case DISCONNECT:
				 		DisconnectMessageType dmt = recievedMes.getDisconnectMessage();
				 		System.out.println("Login failed ... " + dmt.getErroCode()+"\n Client wird geschlossen.");
				 		return false;
				 	default:
				 		System.out.println("Falsche Message empfangen... " + recievedMes.getMcType().toString());
				 		break;
				 }
			 }
			 
			ki = new KI(id);
			
		} catch (JAXBException e) {System.out.println("JAXB-Exception: "+ e.getLocalizedMessage());
		} catch (IOException e) {System.out.println("IO-Exception: "+ e.getLocalizedMessage());
		}
			
		return true;		
	}

	/**
	 * Endlosschleife in der die empfangenen Nachrichten entsprechend bearbeitet werden
	 * @throws JAXBException
	 * @throws IOException
	 */
	private void bearbeite() throws JAXBException, IOException {
		boolean fehler = false;
		while(true){
			MazeCom nachricht = empfangeMessage();
			MazeComType typ = nachricht.getMcType();
			switch (typ) {
				case ACCEPT:
					AcceptMessageType accept = nachricht.getAcceptMessage();
					if (accept.isAccept()){
						// alles erfolgreich
						System.out.println("ACCEPT-Message + accept true");
					}
					else {
						switch(accept.getErrorCode()){
							case AWAIT_MOVE:
								System.out.println("ACCEPT-Message + AWAIT_MOVE-Type");
								// letzten zug noch mal schicken (Es wurde eine andere Nachricht empfangen)
								//sende(ki.berechneZug(nachricht.getAwaitMoveMessage()));
//								MoveMessageType move  = ki.randomZug(nachricht.getAwaitMoveMessage());
//								sende(move);
								break;
							case ILLEGAL_MOVE:
								System.out.println("ACCEPT-Message + ILLEGAL_MOVE-Type");
								// neuen Zug berechnen
								//sende(ki.neuerZug());
								//sende(move1);
								fehler = true;
								break;
							default:
								// sollte eig (laut Protokoll) nicht gebraucht werden
						 		System.out.println("Falsche Message empfangen... " + nachricht.getMcType().toString());
								break;
						} // Ende Switch (AcceptMessageType)
					} //Ende if Abfrage (Accept)	
					break;
					
				case DISCONNECT:
					//Client wird geschlossen (Too many Tries)
					DisconnectMessageType dmt = nachricht.getDisconnectMessage();
			 		System.out.println("Login failed ... " + dmt.getErroCode()+"\n Client wird geschlossen.");
			 		beende();
					break;
					
				case AWAITMOVE:
					System.out.println("AWAIT_MOVE-Message");
					//Erwartet Zug
					//sende(ki.berechneZug(nachricht.getAwaitMoveMessage()));
					
					MoveMessageType move = null;
					
					if (fehler){
						move = ki.neuerZug(nachricht.getAwaitMoveMessage());
						fehler = false;
					}
					else {
						move  = ki.randomZug(nachricht.getAwaitMoveMessage());
					}
					sende(move);
					break;

				case WIN:
					// gewinner ausgeben
					WinMessageType wmt = nachricht.getWinMessage();
					int id_winner = wmt.getWinner().getId();
					if (this.id == id_winner)
						System.out.println("Ich habe gewonnen!!! :-)");
					else 
						System.out.println("Der Spieler mit der ID "+id_winner+" hat gewonnen...");
					break;
		
				default:
					// sollte eig (laut Protokoll) nicht gebraucht werden
			 		System.out.println("Falsche Message empfangen... " + nachricht.getMcType().toString());
					break;
			} //Ende switch (MessageType)
		} // ende while(true)
	}
	
	
	/**
	 * Sendet eine MoveMessage an den Server
	 * es werden nur MoveMessages verschickt, da der Login vorher abgeschlossen ist. 
	 * @throws JAXBException 
	 * @throws IOException 
	 */
	public void sende(MoveMessageType mes) throws JAXBException, IOException{
		MazeCom send = of.createMazeCom();
		send.setId(this.id);
		send.setMcType(MazeComType.MOVE);
		send.setMoveMessage(mes);

		StringWriter strWrite = new StringWriter();
		m.marshal(send,strWrite);
		outToServer.writeUTF8(strWrite.toString());	
			
		System.out.println("gesendet");
//		System.out.println("1");
//		send.setMcType(MazeComType.fromValue("MOVE"));
//		send.setId(id);
//		System.out.println("2");
//		send.setMoveMessage(mes);
//		System.out.println("4");
//		StringWriter strWrite = new StringWriter();
//		// Hier wird ein Object "Message" in den out-Stream geschrieben/gemarshaled
//		System.out.println("5");
//		m.marshal(mes,strWrite);
//		System.out.println("6");
//		outToServer.writeUTF8(strWrite.toString());	
//		System.out.println("gesendet");
	}
	
	/**
	 * Empfaengt eine Nachricht vom Server
	 * @return empfange Nachricht
	 * @throws JAXBException
	 * @throws IOException
	 */
	public MazeCom empfangeMessage() throws JAXBException, IOException{
		MazeCom recievedMes = null;
		StringReader strRead = new StringReader(inFromServer.readUTF8());
		// Hier wird ein Object recieve erstellt, das aus dem InputStream ge"unmarshal"t wird
		recievedMes= (MazeCom)um.unmarshal(strRead);
		return recievedMes;
	}//Ende empfangeMessage()

	
	/**
	 * Beendet den Client
	 */
	public void beende(){
		System.out.println("Client wird beendet...");
		try {
			client.close();
		} catch (IOException e) { e.printStackTrace(); }		
	}

	
	/**
	 * NICHT MEHR VERWENDEN! ANDERS GEL�ST
	 * Bearbeitung der AwaitMove Nachricht
	 * @param maze AwaitMoveNachricht
	 * @deprecated
	 */
	@SuppressWarnings("unused")
	public void empfangeAwait(MazeCom maze){
		AwaitMoveMessageType await = maze.getAwaitMoveMessage();
		
		BoardType board = await.getBoard();
		List <TreasuresToGoType> treasures2go = await.getTreasuresToGo();
		TreasureType treasure = await.getTreasure();
		
		System.out.println("Wert des Schatzes: "+treasure.value());	
	}
	
} //end of class
